跳到主要内容

MyBatis 执行流程

这篇只讲 Mybatis 的基本用法,一些详细的配置之类的看其他的

什么是 MyBatis

文档链接

MyBatis 是一款 持久层 框架,对 jdbc 进行了封装,屏蔽了 jdbc api 底层访问细节,核心思想就是 ORM(对象关系映射),Mybatis 框架执行 SQL 语句并将结果映射为 java 对象返回出去

注意:Mapper 和 Dao 是一个东西,所以既可以叫做 UserMapper 又可以叫 UserDao 具体看规范

配置环境

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>

<!-- MySQL的驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.20</version>
</dependency>

<!-- 单元项目 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>

<!-- 日志系统 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>

MyBatis 开发流程

1、编写 User 实体类

2、编写映射文件 UserMapper.xml(里面主要写一些 SQL 语句)

3、编写核心配置文件 SqlMapConfig.xml

4、编写测试类

原先使用 JDBC 连接

如下,只是执行一个查询操作就要写一大坨代码,所以本着能偷懒则偷懒的原则使用 Mybatis 进行数据库操作

/**
* @author alsritter
* @version 1.0
**/
public class JdbcDemo1 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1. 加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2. 用户信息和url
String url = "jdbc:mysql://localhost:3306/studyJDBC?useUnicode=true&characterEncoding=utf8&useSSL=true&useServerPrepStmts=true";
String username = "root";
String password = "123";
//3.连接成功,数据库对象
Connection connection = DriverManager.getConnection(url,username,password);
//4.执行SQL的对象
Statement statement = connection.createStatement();
//5.执行SQL的对象,查看结果
String sql = "SELECT * FROM users";
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()){
System.out.println("id="+resultSet.getObject("id"));
System.out.println("name="+resultSet.getObject("username"));
System.out.println("pwd="+resultSet.getObject("password"));
System.out.println("email="+resultSet.getObject("email"));
System.out.println("birthday="+resultSet.getObject("birthday"));
}
//6.释放连接
resultSet.close();
statement.close();
connection.close();
}
}

加载资源路径的问题

参考资料 Java资源文件和路径相关扫盲

Java 项目中加载资源文件通常有两种方式:

Class.getResource(String path)

Class.getClassLoader().getResource(String path)

// 输出测试
System.out.println(TestClass.class.getResource(""));
System.out.println(TestClass.class.getClassLoader().getResource(""));

//输出的路径--->
// file:/D:/JavaProject/StudyMyBatis/mybatis01/target/classes/com/alsritter/utils/
// file:/D:/JavaProject/StudyMyBatis/mybatis01/target/classes/

由上的输出可以看出,如果是绝对路径,则绝对路径的起点为 ClassPath,如果为相对路径,则路径的起点为该类的所在目录,就是TestClass所在的目录。

不过读取配置文件有个很方便的全局方法:

// 直接这样写就好了
InputStream fileReader = JdbcUtils.class.getResourceAsStream("/urlPath.properties");

Maven 资源过滤问题

Maven 一般只会把 Resources 目录下的文件丢到其他 classes 目录里面去,其他目录的都不管

所以最好对 Maven 的资源构建进行配置

<!--在build中配置resources,来防止我们资源导出失败的问题-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>

MyBatis 执行流程

每个基于 MyBatis 的应用都是以一个 SqlSessionFactory(会话工厂) 的实例为核心的。

SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。

SqlSessionFactoryBuilder(建造者模式) 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。

所以流程就是: SqlSessionFactoryBuilder → SqlSessionFactory → SqlSession

tvzao8.png

编写的工具类可以看出 这些类的生命周期和作用域

public class MyBatisUtils {

private static SqlSessionFactory sqlSessionFactory ;

static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}

public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}

SqlSessionFactoryBuilder

SqlSessionFactoryBuilder 就是为了创建 SqlSessionFactory 实例的,一旦创建了 SqlSessionFactory,就不再需要它了

因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法体内,用完就销毁掉

SqlSessionFactory

SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,所以一般使用 单例模式

SqlSession

每个线程都有它自己的 SqlSession 实例。且 SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。

try (SqlSession session = sqlSessionFactory.openSession()) {
BlogMapper mapper = session.getMapper(BlogMapper.class);
// 你的应用逻辑代码
}

不能将 SqlSession 实例的引用放在一个类的静态域

也不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession,一般就是使用和 Request 一个作用域,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。

这个关闭操作很重要,为了确保每次都能执行关闭操作,应该把这个关闭操作放到 finally 块中。

或者使用 java 的语法糖

try (SqlSession session = sqlSessionFactory.openSession()) {
// 你的应用逻辑代码
}

在所有代码中都遵循这种使用模式,可以保证所有数据库资源都能被正确地关闭。

Mapper

映射器接口的实例是从 SqlSession 中获得的

映射器实例的最大作用域与请求它们的 SqlSession 相同。

映射器实例应该在调用它们的方法中被获取,使用完毕之后即可丢弃。

映射器实例并不需要被显式地关闭

try (SqlSession session = sqlSessionFactory.openSession()) {
BlogMapper mapper = session.getMapper(BlogMapper.class);
// 你的应用逻辑代码
}